const { getCaseKnex } = require('../database/knex');

/**
 * FundsAnalysisPlugin
 * 资金穿透分析插件
 */
const FundsAnalysisPlugin = {
    id: 'funds-analysis',
    name: '资金穿透分析',
    version: '1.0.0',
    description: '分析指定账户的资金流向，支持多层穿透',
    type: 'analysis',
    params: [
        { key: 'depth', label: '穿透层级', type: 'number', default: 3, min: 1, max: 10 },
        { key: 'minAmount', label: '最小金额', type: 'number', default: 0, min: 0, step: 100 },
        {
            key: 'direction', label: '穿透方向', type: 'select', options: [
                { value: 'out', label: '资金流出 (向后穿透)' },
                { value: 'in', label: '资金流入 (向前溯源)' }
            ], default: 'out'
        },
        { key: 'account', label: '手动指定卡号', type: 'string', placeholder: '如不填则使用上方选择的对象', description: '仅在全量模式下或需要覆盖选择对象时填写' }
    ],

    /**
     * 执行分析
     * @param {Object} context 上下文 (包含 dbPath 等)
     * @param {Object} params 参数 (account, depth, etc.)
     */
    run: async (context, params) => {
        const { caseId, dbPath } = context;
        // Support 'account' (legacy), 'accounts' (array), or 'accounts' object (from SubjectService)
        let targetAccounts = [];
        if (params.accounts) {
            if (Array.isArray(params.accounts)) {
                targetAccounts = params.accounts;
            } else if (typeof params.accounts === 'object' && Array.isArray(params.accounts.bank)) {
                targetAccounts = params.accounts.bank;
            }
        }

        if (params.account) {
            targetAccounts.push(params.account);
        }

        // Deduplicate
        targetAccounts = [...new Set(targetAccounts)];

        const {
            depth = 3,
            minAmount = 0,
            direction = 'out',
            startDate,
            endDate,
            subjectName
        } = params;

        if (!caseId || !dbPath) {
            throw new Error('Missing required parameters: caseId or dbPath');
        }

        const knex = getCaseKnex(caseId, dbPath);

        // Fallback: If no accounts but subjectName exists, query DB
        if (targetAccounts.length === 0 && subjectName) {
            console.log(`[FundsAnalysisPlugin] No accounts provided for ${subjectName}, querying DB by name...`);
            const accounts = await knex('bank_transactions')
                .distinct('card_number')
                .where('customer_name', subjectName)
                .whereNotNull('card_number');

            if (accounts.length > 0) {
                targetAccounts = accounts.map(a => a.card_number);
                console.log(`[FundsAnalysisPlugin] Found ${targetAccounts.length} accounts for ${subjectName}`);
            }
        }

        // Return clear result if no accounts provided (e.g. Global Mode)
        if (targetAccounts.length === 0) {
            const msg = subjectName
                ? `分析对象 "${subjectName}"在该案件中无关联银行账号，无法进行资金穿透。`
                : '资金穿透分析需要指定分析对象或账号';

            return {
                nodes: [],
                links: [],
                message: msg,
                level: 'warning'
            };
        }
        console.log(`[FundsAnalysisPlugin] Tracing ${direction} for ${targetAccounts.length} accounts, depth: ${depth}`);

        const result = {
            nodes: [],
            links: []
        };

        const visited = new Set();
        let currentLevelAccounts = [];

        // Init start accounts
        for (const acc of targetAccounts) {
            if (!visited.has(acc)) {
                visited.add(acc);
                result.nodes.push({ name: acc, value: 0, depth: 0, isTarget: true });
                currentLevelAccounts.push(acc);
            }
        }

        // Level-based BFS
        for (let level = 0; level < depth; level++) {
            if (currentLevelAccounts.length === 0) break;
            console.log(`[FundsAnalysisPlugin] Level ${level}: Processing ${currentLevelAccounts.length} accounts`);

            // Batch Query
            const query = knex('bank_transactions');

            if (direction === 'out') {
                query.whereIn('card_number', currentLevelAccounts)
                    .where('debit_credit_flag', '借');
            } else {
                query.whereIn('counterparty_account', currentLevelAccounts)
                    .where('debit_credit_flag', '借');
            }

            if (minAmount > 0) {
                query.where(knex.raw('ABS(transaction_amount)'), '>=', minAmount);
            }
            if (startDate) {
                query.where('transaction_time', '>=', startDate);
            }
            if (endDate) {
                query.where('transaction_time', '<=', endDate);
            }

            // Group and Aggregate
            // We need Source AND Target to map back
            const selectCols = [
                'card_number',
                'counterparty_account',
                direction === 'out' ? 'counterparty_name as target_name' : 'customer_name as source_name',
                knex.raw('SUM(ABS(transaction_amount)) as total_amount'),
                knex.raw('COUNT(*) as count')
            ];

            // Note: Grouping by both source and target/counterparty to get specific links
            query.select(selectCols)
                .whereNotNull(direction === 'out' ? 'counterparty_account' : 'card_number')
                .groupBy('card_number', 'counterparty_account');

            const transactions = await query;
            console.log(`[FundsAnalysisPlugin] Level ${level}: Retrieved ${transactions.length} raw flows`);

            // Process results in memory to limit branching factor (Top 20 per source)
            const nextLevelAccounts = new Set();
            const flowsBySource = new Map(); // SourceAccount -> [Flows]

            for (const tx of transactions) {
                const source = direction === 'out' ? tx.card_number : tx.counterparty_account; // The account currently in our 'currentLevelAccounts'
                if (!flowsBySource.has(source)) {
                    flowsBySource.set(source, []);
                }
                flowsBySource.get(source).push(tx);
            }

            // Sort and Limit
            for (const [source, flows] of flowsBySource) {
                // Sort by amount desc
                flows.sort((a, b) => b.total_amount - a.total_amount);
                const topFlows = flows.slice(0, 20); // Limit branching

                for (const flow of topFlows) {
                    const nextAccount = direction === 'out' ? flow.counterparty_account : flow.card_number;
                    const nextName = direction === 'out' ? flow.target_name : flow.source_name;
                    const amount = flow.total_amount;
                    const count = flow.count;

                    // Add node if not exists
                    if (!visited.has(nextAccount)) {
                        visited.add(nextAccount);
                        result.nodes.push({
                            name: nextAccount,
                            label: nextName || nextAccount,
                            value: amount,
                            depth: level + 1
                        });
                        nextLevelAccounts.add(nextAccount);
                    }

                    // Add link
                    result.links.push({
                        source: direction === 'out' ? source : nextAccount,
                        target: direction === 'out' ? nextAccount : source,
                        value: amount,
                        count: count
                    });
                }
            }

            currentLevelAccounts = Array.from(nextLevelAccounts);
        }

        console.log(`[FundsAnalysisPlugin] Analysis complete. Nodes: ${result.nodes.length}, Links: ${result.links.length}`);
        return result;
    }
};

module.exports = FundsAnalysisPlugin;
